﻿using System;
using System.Web.Mvc;
using Microsoft.Practices.ServiceLocation;

namespace Presentation.Web.UI.Attibutes.Authorize
{
    /// <summary>
    /// Filter attribute that wraps an inner filter. The inner filter is constructed by the ServiceLocator for the current IoC container.
    /// Eg FilterUsing(typeof(MyFilter)) where MyFilter implements IActionFilter and is registered with the container.
    /// </summary>
    public class FilterUsingAttribute : FilterAttribute, IAuthorizationFilter, IActionFilter, IResultFilter
    {
        private readonly Type _filterType;
        private object _instantiatedFilter;

        public FilterUsingAttribute(Type filterType)
        {
            if (!IsFilterType(filterType))
            {
                throw new InvalidOperationException(String.Format("Type '{0}' is not valid within the FilterUsing attribute as it is not a filter type.", filterType.Name));
            }
            this._filterType = filterType;
        }

        private bool IsFilterType(Type type)
        {
            return typeof(IAuthorizationFilter).IsAssignableFrom(type)
                   || typeof(IActionFilter).IsAssignableFrom(type)
                   || typeof(IResultFilter).IsAssignableFrom(type);
        }

        public Type FilterType
        {
            get { return _filterType; }
        }

        private T GetFilter<T>() where T : class
        {
            if (_instantiatedFilter == null)
            {
                _instantiatedFilter = ServiceLocator.Current.GetInstance(_filterType);
            }

            return _instantiatedFilter as T;
        }

        private void ExecuteFilterWhenItIs<TFilter>(Action<TFilter> action) where TFilter : class
        {
            var filter = GetFilter<TFilter>();

            if (filter != null)
            {
                action(filter);
            }
        }

        public void OnAuthorization(AuthorizationContext filterContext)
        {
            ExecuteFilterWhenItIs<IAuthorizationFilter>(f => f.OnAuthorization(filterContext));
        }

        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            ExecuteFilterWhenItIs<IResultFilter>(f => f.OnResultExecuting(filterContext));
        }

        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            ExecuteFilterWhenItIs<IResultFilter>(f => f.OnResultExecuted(filterContext));
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            ExecuteFilterWhenItIs<IActionFilter>(f => f.OnActionExecuting(filterContext));
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            ExecuteFilterWhenItIs<IActionFilter>(f => f.OnActionExecuted(filterContext));
        }
    }
}
